﻿using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Text;
using Microsoft.CodeAnalysis.Text;

namespace NetCorePal.Extensions.DistributedTransactions.CAP.SourceGenerators
{
    [Generator]
    public class CapSubscriberSourceGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            // 1. 配置选项管道
            var configOptions = context.AnalyzerConfigOptionsProvider
                .Select(static (options, _) => 
                    options.GlobalOptions.TryGetValue("build_property.RootNamespace", out var ns) 
                        ? ns 
                        : null);

            // 2. 语义分析管道 
            var handlerSymbols = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => 
                        node is TypeDeclarationSyntax { BaseList.Types.Count: > 0 },
                    transform: static (ctx, _) => GetTypeSymbol(ctx))
                .Where(static symbol => 
                    symbol?.AllInterfaces.Any(i => i.Name == "IIntegrationEventHandler") == true)
                .Combine(context.CompilationProvider)
                .Combine(configOptions)
                .WithTrackingName("HandlerSymbolPipeline");

            // 3. 注册生成逻辑
            context.RegisterSourceOutput(handlerSymbols,
                (ctx, tuple) => GenerateCode(
                    ctx,
                    tuple.Left.Left,  // INamedTypeSymbol
                    tuple.Left.Right, // Compilation
                    tuple.Right));    // RootNamespace
        }

        private static INamedTypeSymbol? GetTypeSymbol(GeneratorSyntaxContext context)
        {
            var typeDeclaration = (TypeDeclarationSyntax)context.Node;
            return context.SemanticModel.GetDeclaredSymbol(typeDeclaration) as INamedTypeSymbol;
        }

        private static void GenerateCode(
            SourceProductionContext context,
            INamedTypeSymbol? eventHandlerTypeSymbol,
            Compilation compilation,
            string? rootNamespace)
        {
            if (string.IsNullOrWhiteSpace(rootNamespace)) return;
            if (eventHandlerTypeSymbol is null) return;
            var className = eventHandlerTypeSymbol.Name;
            var attr = eventHandlerTypeSymbol.GetAttributes()
                .FirstOrDefault(p => p.AttributeClass!.Name == "IntegrationEventConsumerAttribute");

            //根据dbContextType继承的接口IIntegrationEventHandle<TIntegrationEvent> 推断出TIntegrationEvent类型
            var typeArgument = eventHandlerTypeSymbol.AllInterfaces
                .FirstOrDefault(p => p.Name == "IIntegrationEventHandler")?.TypeArguments.FirstOrDefault();

            if (typeArgument == null)
            {
                return;
            }

            var eventName = attr?.ConstructorArguments[0].Value?.ToString();
            if (string.IsNullOrWhiteSpace(eventName))
            {
                eventName = typeArgument.Name;
            }

            var groupName = attr?.ConstructorArguments[1].Value?.ToString();
            if (!string.IsNullOrEmpty(groupName))
            {
                groupName = $@", Group = ""{groupName}""";
            }

            string source = $@"// <auto-generated/>
using DotNetCore.CAP;
using NetCorePal.Extensions.Repository;
using NetCorePal.Extensions.Repository.EntityFrameworkCore;
using NetCorePal.Extensions.DistributedTransactions;
using {eventHandlerTypeSymbol.ContainingNamespace};
namespace {rootNamespace}.Subscribers
{{
    /// <summary>
    /// {className}AsyncSubscriber
    /// </summary>
    public class {className}AsyncSubscriber : ICapSubscribe
    {{
        readonly ITransactionUnitOfWork _unitOfWork;
        readonly IntegrationEventHandlerWrap<{className}, {typeArgument?.ContainingNamespace}.{typeArgument?.Name}> _handler;
        
        /// <summary>
        /// {className}AsyncSubscriber
        /// </summary>
        /// <param name=""unitOfWork"">unitOfWork</param>
        /// <param name=""handler"">handler</param>
        public {className}AsyncSubscriber(ITransactionUnitOfWork unitOfWork, IntegrationEventHandlerWrap<{className}, {typeArgument?.ContainingNamespace}.{typeArgument?.Name}> handler)
        {{
            _unitOfWork = unitOfWork;
            _handler = handler;
        }}
        
        /// <summary>
        /// ProcessAsync
        /// </summary>
        /// <param name=""message"">message</param>
        /// <param name=""headers"">headers</param>
        /// <param name=""cancellationToken"">cancellationToken</param>
        [CapSubscribe(""{eventName}""{groupName})]
        public Task ProcessAsync({typeArgument?.ContainingNamespace}.{typeArgument?.Name} message, [FromCap]CapHeader headers, CancellationToken cancellationToken)
        {{
            return _handler.HandleAsync(message, headers, cancellationToken);
        }}
    }}
}}
";
            context.AddSource($"{className}AsyncSubscriber.g.cs", SourceText.From(source, Encoding.UTF8));
        }
    }
}